﻿#region License
/* ---------------------------------------------------------------- *
 *
 * This file is part of the API Concepts Framework
 * http://apiconceptsframework.codeplex.com
 *
 * Copyright (C) 2014 eva Kühn, Thomas Scheller, Vienna University of Technology
 * http://www.complang.tuwien.ac.at/eva
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/
 *
 * ---------------------------------------------------------------- */
#endregion

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using ApiConceptsFramework.Contract;

namespace ApiConceptsFramework.FluentApi
{
	/// <summary>
	/// ApiEvaluator is the main class for API evaluation.
	/// </summary>
	public class ApiEvaluator
	{
		private readonly List<Assembly> _apiAssemblies;
		private readonly List<ConceptInstance> _data = new List<ConceptInstance>();
		private readonly List<IHcEvaluationInput> _hcInputData = new List<IHcEvaluationInput>(); 
		private List<string> _usings;
		private List<Assembly> _referencedAssemblies;

		/// <summary>
		/// The IDE for which the evaluation is made. Default is VisualStudioOrSimilar.
		/// </summary>
		public IDE IDE { get; set; }

		/// <summary>
		/// Sets the output format, i.e. how the evaluation output should be formatted (default is Console).
		/// </summary>
		public EvaluatorOutputFormat OutputFormat { get; set; }
		
		/// <summary>
		/// The data that has already been added via the fluent methods.
		/// </summary>
		public IEnumerable<ConceptInstance> Data
		{
			get { return _data; }
		}

		/// <summary>
		/// Creates a new ApiEvaluator.
		/// </summary>
		/// <param name="apiAssemblies">The list of assemblies that are part of the API.</param>
		public ApiEvaluator(params Assembly[] apiAssemblies)
		{
			_apiAssemblies = new List<Assembly>(apiAssemblies);
			_referencedAssemblies = new List<Assembly>(apiAssemblies); //set API assemblies as the referenced assemblies by default (if additional assemblies are referenced, SetAssemblyRefs needs to be called)
			IDE = IDE.VisualStudioOrSimilar;
		}

		/// <summary>
		/// Sets the list of assemblies that are referenced in the code that uses the API. This
		/// list is e.g. used to evaluate which extension methods are visible to the user.
		/// </summary>
		/// <param name="assemblies">The list of referenced assemblies.</param>
		public void SetAssemblyRefs(params Assembly[] assemblies)
		{
			_referencedAssemblies = new List<Assembly>(assemblies);
		}

		/// <summary>
		/// Sets the usings, which are part of the code that uses the API. E.g. for "using XcoAppSpaces.Core",
		/// one would give "XcoAppSpaces.Core" as a using into this method. The list is used to to evaluate 
		/// which extension methods are visible to the user.
		/// </summary>
		/// <param name="usings">The list of usings.</param>
		public void SetUsings(params string[] usings)
		{
			_usings = new List<string>(usings);
		}

		///// <summary>
		///// Defines that a certain class is used. ClassUsage should be defined always when a class name directly
		///// appears in the code, e.g. when a class is instantiated, or a static method is called.
		///// </summary>
		///// <typeparam name="T">The type of class that is used.</typeparam>
		///// <param name="numberOfUsages">The number of usages (default = 1).</param>
		///// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		///// <returns>Object for further fluent configuration.</returns>
		//public Tuple<ApiEvaluator,ConceptInstance> ClassUsage<T>(int numberOfUsages = 1, int previousUsages = 0)
		//{
		//	var c = new ConceptInstance
		//		{
		//			Concept = Concepts.ClassUsage,
		//			NumberOfUsages = numberOfUsages,
		//			PreviousUsages = previousUsages,
		//			Target = new MeasurableTarget { Type = typeof(T), CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies },
		//		};
		//	AddOrUpdate(c);
		//	return Tuple.Create(this, c);
		//}

		private void AddOrUpdate(ConceptInstance data)
		{
			var existing = _data.FirstOrDefault(d =>
				d.Concept == data.Concept &&
				d.Target.Type == data.Target.Type &&
				d.Target.Member == data.Target.Member &&
				d.Target.ReturnedValueIsUsed == data.Target.ReturnedValueIsUsed);
			if (existing == null)
				_data.Add(data);
			else
			{
				existing.NumberOfUsages += data.NumberOfUsages;
				if (existing.PreviousUsages < data.PreviousUsages)
					existing.PreviousUsages = data.PreviousUsages; //is this the right behaviour in such a case? or would throwing an error be better?
				//if (existing.PreviousUsages != data.PreviousUsages)
				//	throw new ArgumentException("Cannot add evaluation data: Number of previous usages differs with same already existing data. Target: " + data.Target.ToPrintableString());
			}
		}

		/// <summary>
		/// Defines that a certain class is used. ClassUsage should be defined always when a class name directly
		/// appears in the code, e.g. when a class is instantiated, or a static method is called.
		/// Is automatically called in addition to the appropriate concepts
		/// </summary>
		/// <param name="type">The type of class that is used.</param>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <returns>Object for further fluent configuration.</returns>
		internal Tuple<ApiEvaluator, ConceptInstance> ClassUsage(Type type, int numberOfUsages = 1, int previousUsages = 0)
		{
			var c = new ConceptInstance
			{
				Concept = Concepts.ClassUsage,
				NumberOfUsages = numberOfUsages,
				PreviousUsages = previousUsages,
				Target = new MeasurableTarget { Type = type, CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies },
			};
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Defines that a class is instantiated using a certain constructor. Instantiation should additionally always lead to calling ClassUsage.
		/// </summary>
		/// <typeparam name="T">The type of class that is used.</typeparam>
		/// <param name="instantiationExpr">Expression that defines which constructor is used.</param>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <returns>Object for further fluent configuration.</returns>
		public Tuple<ApiEvaluator, ConceptInstance> Instantiation<T>(Expression<Func<T>> instantiationExpr, int numberOfUsages = 1, int previousUsages = 0)
		{
			var c = new ConceptInstance
				{
					Concept = Concepts.Instantiation,
					NumberOfUsages = numberOfUsages,
					PreviousUsages = previousUsages,
					Target = new MeasurableTarget { Type = typeof(T), Member = instantiationExpr.GetConstructorInfo(), ReturnedValueIsUsed = true, CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies }
				};
			ClassUsage(typeof(T), numberOfUsages, previousUsages);
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Defines that a certain method is called. If the method is static, additionally ClassUsage should be called.
		/// </summary>
		/// <typeparam name="T">The type of class that is used.</typeparam>
		/// <param name="methodCallExpr">Expression that defines which method is called.</param>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <param name="returnParameterIsUsed">Needs to be set to true if the return value is actually used in the code (because this makes the concept more expensive).</param>
		/// <returns>Object for further fluent configuration.</returns>
		public Tuple<ApiEvaluator, ConceptInstance> MethodCall<T>(Expression<Action<T>> methodCallExpr, int numberOfUsages = 1, int previousUsages = 0, bool returnParameterIsUsed = true)
		{
			var method = methodCallExpr.GetMethodInfo();
			var c = new ConceptInstance
				{
					Concept = Concepts.MethodCall,
					NumberOfUsages = numberOfUsages,
					PreviousUsages = previousUsages,
					Target = new MeasurableTarget { Type = typeof(T), Member = method, ReturnedValueIsUsed = returnParameterIsUsed, CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies }
				};
			if (method.IsStatic && !method.IsExtensionMethod())
				ClassUsage(typeof(T), numberOfUsages, previousUsages);
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Defines that a certain static method is called. For static methods additionally ClassUsage should be called.
		/// </summary>
		/// <param name="methodCallExpr">Expression that defines which method is called.</param>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <param name="returnParameterIsUsed">Needs to be set to true if the return value is actually used in the code (because this makes the concept more expensive).</param>
		/// <returns>Object for further fluent configuration.</returns>
		public Tuple<ApiEvaluator, ConceptInstance> MethodCall(Expression<Action> methodCallExpr, int numberOfUsages = 1, int previousUsages = 0, bool returnParameterIsUsed = true)
		{
			var method = methodCallExpr.GetMethodInfo();
			var c = new ConceptInstance
			{
				Concept = Concepts.MethodCall,
				NumberOfUsages = numberOfUsages,
				PreviousUsages = previousUsages,
				Target = new MeasurableTarget { Type = method.DeclaringType, Member = method, ReturnedValueIsUsed = returnParameterIsUsed, CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies }
			};
			if (method.IsStatic && !method.IsExtensionMethod())
				ClassUsage(method.DeclaringType, numberOfUsages, previousUsages);
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Defines that a certain field is accessed. If the field is static, additionally ClassUsage should be called.
		/// </summary>
		/// <typeparam name="T">The type of class that is used.</typeparam>
		/// <typeparam name="TField">The type of the field that is accessed.</typeparam>
		/// <param name="fieldAccessExpr">Expression that defines which field is accessed.</param>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <returns>Object for further fluent configuration.</returns>
		public Tuple<ApiEvaluator, ConceptInstance> FieldAccess<T, TField>(Expression<Func<T, TField>> fieldAccessExpr, int numberOfUsages = 1, int previousUsages = 0)
		{
			var field = fieldAccessExpr.GetFieldInfo();
			var c = new ConceptInstance
				{
					Concept = Concepts.FieldAccess,
					NumberOfUsages = numberOfUsages,
					PreviousUsages = previousUsages,
					Target = new MeasurableTarget { Type = typeof(T), Member = field, ReturnedValueIsUsed = true, CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies }
				};
			if (field.IsStatic)
				ClassUsage(typeof(T), numberOfUsages, previousUsages);
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Defines that a certain static field is accessed. For static fields additionally ClassUsage should be called.
		/// </summary>
		/// <typeparam name="TField">The type of the field that is accessed.</typeparam>
		/// <param name="fieldAccessExpr">Expression that defines which field is accessed.</param>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <returns>Object for further fluent configuration.</returns>
		public Tuple<ApiEvaluator, ConceptInstance> FieldAccess<TField>(Expression<Func<TField>> fieldAccessExpr, int numberOfUsages = 1, int previousUsages = 0)
		{
			var field = fieldAccessExpr.GetFieldInfo();
			var c = new ConceptInstance
			{
				Concept = Concepts.FieldAccess,
				NumberOfUsages = numberOfUsages,
				PreviousUsages = previousUsages,
				Target = new MeasurableTarget { Type = field.DeclaringType, Member = field, ReturnedValueIsUsed = true, CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies }
			};
			if (field.IsStatic)
				ClassUsage(field.DeclaringType, numberOfUsages, previousUsages);
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Defines that a certain property is accessed. If the property is static, additionally ClassUsage should be called.
		/// </summary>
		/// <typeparam name="T">The type of class that is used.</typeparam>
		/// <typeparam name="TField">The type of the property that is accessed.</typeparam>
		/// <param name="propertyAccessExpr">Expression that defines which property is accessed.</param>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <returns>Object for further fluent configuration.</returns>
		public Tuple<ApiEvaluator, ConceptInstance> PropertyAccess<T, TField>(Expression<Func<T, TField>> propertyAccessExpr, int numberOfUsages = 1, int previousUsages = 0)
		{
			var property = propertyAccessExpr.GetPropertyInfo();
			var c = new ConceptInstance
				{
					Concept = Concepts.FieldAccess,
					NumberOfUsages = numberOfUsages,
					PreviousUsages = previousUsages,
					Target = new MeasurableTarget { Type = typeof(T), Member = property, ReturnedValueIsUsed = true, CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies }
				};
			if (property.GetAccessors(true).First().IsStatic)
				ClassUsage(typeof(T), numberOfUsages, previousUsages);
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Defines that a certain static property is accessed. For static properties additionally ClassUsage should be called.
		/// </summary>
		/// <typeparam name="TField">The type of the property that is accessed.</typeparam>
		/// <param name="propertyAccessExpr">Expression that defines which property is accessed.</param>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <returns>Object for further fluent configuration.</returns>
		public Tuple<ApiEvaluator, ConceptInstance> PropertyAccess<TField>(Expression<Func<TField>> propertyAccessExpr, int numberOfUsages = 1, int previousUsages = 0)
		{
			var property = propertyAccessExpr.GetPropertyInfo();
			var c = new ConceptInstance
			{
				Concept = Concepts.FieldAccess,
				NumberOfUsages = numberOfUsages,
				PreviousUsages = previousUsages,
				Target = new MeasurableTarget { Type = property.DeclaringType, Member = property, ReturnedValueIsUsed = true, CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies }
			};
			if (property.GetAccessors(true).First().IsStatic)
				ClassUsage(property.DeclaringType, numberOfUsages, previousUsages);
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Defines that a certain property is accessed. This is an overload for setter-only properties, which cannot be accessed that 
		/// easily in an expression tree. If the property is static, additionally ClassUsage should be called.
		/// </summary>
		/// <typeparam name="T">The type of class that is used.</typeparam>
		/// <param name="propertyName">The name of the property.</param>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <returns>Object for further fluent configuration.</returns>
		public Tuple<ApiEvaluator, ConceptInstance> PropertyAccess<T>(string propertyName, int numberOfUsages = 1, int previousUsages = 0)
		{
			var property = typeof(T).GetProperty(propertyName);
			var c = new ConceptInstance
			{
				Concept = Concepts.FieldAccess,
				NumberOfUsages = numberOfUsages,
				PreviousUsages = previousUsages,
				Target = new MeasurableTarget { Type = typeof(T), Member = property, ReturnedValueIsUsed = true, CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies }
			};
			if (property.GetAccessors(true).First().IsStatic)
				ClassUsage(typeof(T), numberOfUsages, previousUsages);
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Defines that a certain interface must be implemented. InterfaceImplementation should additionally always lead to calling ClassUsage.
		/// </summary>
		/// <typeparam name="T">The type of interface that is used.</typeparam>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <returns>Object for further fluent configuration.</returns>
		public Tuple<ApiEvaluator, ConceptInstance> InterfaceImplementation<T>(int numberOfUsages = 1, int previousUsages = 0)
		{
			if (!(typeof(T).IsInterface))
				throw new ArgumentException(typeof(T).Name + " is not an interface.");

			var c = new ConceptInstance
			{
				Concept = Concepts.InterfaceImplementation,
				NumberOfUsages = numberOfUsages,
				PreviousUsages = previousUsages,
				Target = new MeasurableTarget { Type = typeof(T), Member = null, CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies }
			};
			ClassUsage(typeof(T), numberOfUsages, previousUsages);
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Defines that a certain class is inherited. Inheritance should additionally always lead to calling ClassUsage.
		/// </summary>
		/// <typeparam name="T">The type of class that is used.</typeparam>
		/// <param name="baseConstructorExpression">Expression defining the base constructor that is called (currently only works with a public base constructor).</param>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <returns>Object for further fluent configuration.</returns>
		public Tuple<ApiEvaluator, ConceptInstance> Inheritance<T>(Expression<Func<T>> baseConstructorExpression, int numberOfUsages = 1, int previousUsages = 0)
		{
			//TODO provide method for inheritance with protected constructors
			var c = new ConceptInstance
			{
				Concept = Concepts.Inheritance,
				NumberOfUsages = numberOfUsages,
				PreviousUsages = previousUsages,
				Target = new MeasurableTarget { Type = typeof(T), Member = baseConstructorExpression.GetConstructorInfo(), CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies }
			};
			ClassUsage(typeof(T), numberOfUsages, previousUsages);
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Defines that a certain member is implemented. This needs to be called for Inheritance and Interface Implementation,
		/// for each member to be implemented. (currently only works with public members)
		/// </summary>
		/// <typeparam name="T">The type of class/interface that is inherited/implemented.</typeparam>
		/// <param name="memberExpr">Expression defining the method that is implemented.</param>
		/// <param name="numberOfUsages">The number of usages (default = 1).</param>
		/// <param name="previousUsages">The number of previous usages (default = 0), used for calculating the learning effect.</param>
		/// <returns>Object for further fluent configuration.</returns>
		public Tuple<ApiEvaluator, ConceptInstance> MemberImplementation<T>(Expression<Action<T>> memberExpr, int numberOfUsages = 1, int previousUsages = 0)
		{
			//TODO provide method for protected members
			var c = new ConceptInstance
			{
				Concept = Concepts.MemberImplementation,
				NumberOfUsages = numberOfUsages,
				PreviousUsages = previousUsages,
				Target = new MeasurableTarget { Type = typeof(T), Member = memberExpr.GetMethodInfo(), ReturnedValueIsUsed = true, CurrentUsings = _usings, ReferencedAssemblies = _referencedAssemblies }
			};
			AddOrUpdate(c);
			return Tuple.Create(this, c);
		}

		/// <summary>
		/// Runs the evaluation, and prints the results to the console. 
		/// </summary>
		public void EvaluateAndPrintToConsole()
		{
			Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US"); //probably the easiest way to display all decimal separators as dots...

			//HC evaluation
			var hcData = ClearHcDataAndEvaluateHcs();
			var numberOfHcComplexityData = hcData.Where(pd => pd.ComplexityData != null).Sum(pd => pd.ComplexityData.Count);

			//Printing low-level concepts
			Console.WriteLine("Evaluation details: (" + _data.Count + " low-level concepts, " + numberOfHcComplexityData + " high-level concepts)");
			Console.WriteLine(OutputFormat.GetTitleFormat(), "Cost S", "Cost U", "Cost Sum", "Concept", "Usages", "Prev Usages", "Description");
			var lineFormat = OutputFormat.GetLineFormat();
			float sum = 0;
			foreach (var c in _data)
			{
				var searchComplexity = c.CalculateSearchComplexity(IDE);
				var usageComplexity = c.CalculateUsageComplexity(IDE);
				var complexity = searchComplexity + usageComplexity;
				var suggestions = c.Concept.GetSuggestions(c.Target, IDE).ToList();
				Console.WriteLine(lineFormat, searchComplexity, usageComplexity, complexity, c.Concept.GetType().Name, c.NumberOfUsages, c.PreviousUsages, c.Target.ToPrintableString());
				foreach (var suggestion in suggestions)
				{
					Console.WriteLine(lineFormat, "", "", "", "", "", "", suggestion);
				}
				foreach (var p in c.HcData)
				{
					Console.WriteLine(lineFormat, "", "", "", "", "", "", p.HighLevelConcept.GetType().Name + ": " + p.SearchComplexity + " / " + p.UsageComplexity + (!string.IsNullOrEmpty(p.Description) ? (" (" + p.Description + ")") : ""));
					if (p.Suggestions != null)
						foreach (var s in p.Suggestions)
						{
							Console.WriteLine(lineFormat, "", "", "", "", "", "", "  " + s);
						}
				}
				sum += complexity;
			}

			//Printing high-level concepts
			foreach (var p in hcData)
			{
				Console.WriteLine(lineFormat, "", "", p.GeneralComplexity, "", "", "", p.HighLevelConcept.GetType().Name + (p.GeneralDescription != null ? (": " + p.GeneralDescription) : "")); 
				sum += p.GeneralComplexity;
				if (p.GeneralSuggestions != null)
				{
					foreach (var s in p.GeneralSuggestions)
					{
						Console.WriteLine(lineFormat, "", "", "", "", "", "", "  " + s);
					}
				}
				if (p.ComplexityData != null)
				{
					foreach (var cd in p.ComplexityData)
					{
						var complexity = cd.SearchComplexity + cd.UsageComplexity;
						Console.WriteLine(lineFormat, cd.SearchComplexity, cd.UsageComplexity, complexity, cd.HighLevelConcept.GetType().Name, (cd.NumberOfUsages != 0 ? cd.NumberOfUsages.ToString() : ""), "", cd.Description);
						sum += complexity;
						if (cd.Suggestions != null)
							foreach (var s in cd.Suggestions)
							{
								Console.WriteLine(lineFormat, "", "", "", "", "", "", "  " + s);
							}
					}
				}
			}

			Console.WriteLine(OutputFormat.GetResultFormat(), sum);
			Console.WriteLine("Total number of independent actions (concepts*usages): " + CalculateNumberOfIndependentActions(hcData));
		}

		private int CalculateNumberOfIndependentActions(IEnumerable<HcEvaluationResult> hcData)
		{
			var conceptActions = _data
				.Where(c => !(c.Concept is ApiConceptsFramework.Concepts.ClassUsage))
				.Sum(c => c.NumberOfUsages);
			var hcActions = hcData
				.Where(p => p.ComplexityData != null)
				.SelectMany(p => p.ComplexityData)
				.Sum(cd => cd.NumberOfUsages);
			return conceptActions + hcActions;
		}

		private List<HcEvaluationResult> ClearHcDataAndEvaluateHcs()
		{
			_data.ForEach(c => c.HcData.Clear());
			var hcData = new List<HcEvaluationResult>();
			foreach (var p in HighLevelConcepts.List)
			{
				var result = p.Evaluate(_apiAssemblies, _data, GethcInputData(p), hcData);
				if (result != null)
					hcData.Add(result);
			}
			return hcData;
		}

		/// <summary>
		/// This method can be called by extensions to get the input data for a specific high-level concept (pattern).
		/// </summary>
		/// <typeparam name="T">Type of the HC.</typeparam>
		/// <returns>The HC input data.</returns>
		public T GetOrCreateHcInputData<T>() where T : class, IHcEvaluationInput, new()
		{
			//this method can be called by HC-specific extensions to get the input data for their HC
			var pd = _hcInputData.FirstOrDefault(p => p is T) as T;
			if (pd == null)
			{
				pd = new T();
				_hcInputData.Add(pd);
			}
			return pd;
		}

		private IHcEvaluationInput GethcInputData(IHighLevelConcept hc)
		{
			return _hcInputData.FirstOrDefault(p => p.GetType() == hc.InputDataType);
		}

		/// <summary>
		/// Prints a more detailed evaluation of the low-level concepts to the console, with the
		/// cost calculated per each measurable property.
		/// </summary>
		public void PrintConceptEvaluationDetails()
		{
			foreach (var c in _data)
			{
				Console.WriteLine();
				Console.WriteLine(c.Concept.GetType().Name + ":  " + c.Target.ToPrintableString());
				Console.WriteLine("Search Properties:");
				foreach (var prop in c.Concept.SearchProperties)
					Console.WriteLine("  " + prop.GetType().Name + "=" + prop.CalculateComplexity(c.Target, IDE) + 
						(prop.CalculationInfo != null ? (" (" + prop.CalculationInfo + ")") : ""));
				Console.WriteLine("Usage Properties:");
				foreach (var prop in c.Concept.UsageProperties)
					Console.WriteLine("  " + prop.GetType().Name + "=" + prop.CalculateComplexity(c.Target, IDE) +
						(prop.CalculationInfo != null ? (" (" + prop.CalculationInfo + ")") : ""));
			}
		}
	}
}
